package com.ccubee.data.transfer.event;

import com.alibaba.otter.canal.protocol.CanalEntry;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.ccubee.data.transfer.jdbc.JdbcPoolUtil;
import com.ccubee.data.transfer.properties.DataProperties;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.List;

/**
 * 抽象监听类型
 *
 * @Author ycf
 * @Date 2022/5/22 12:02
 * @Version 1.0
 */
@Slf4j
public abstract class AbstractEventType implements IEventType {
    @Resource
    protected DataProperties clientProperties;
    @Autowired(required = false)
    protected DataSource dataSource;

    /**
     * 获取数据源
     *
     * @param cmd 参数
     * @return 数据源
     */
    public DataSource getDataSource(Cmd cmd) {
        return clientProperties.dataSource(cmd.getDataSource());
    }

    /**
     * 执行sql
     *
     * @param cmd     命令属性
     * @param sqlData 数据信息
     */
    public void cmdSql(Cmd cmd, SqlData sqlData) {
        try {
            DataSource dataSource = getDataSource(cmd);
            if (dataSource == null) {
                dataSource = this.dataSource;
            }
            JdbcPoolUtil.executeSql(dataSource, sqlData.getSql(), sqlData.getData());
        } catch (Exception e) {
            log.error("cmd:{}", cmd);
            log.error("data:{}", sqlData);
            log.error(e.getMessage(), e);
        }
    }

    /**
     * insert or update 语句
     *
     * @param insertOrUpdate 是否开启insert or update
     * @param cmd            参数
     * @param columnList     字段/数据列表
     * @return sql数据
     */
    public SqlData formatInsertSql(boolean insertOrUpdate, Cmd cmd, List<CanalEntry.Column> columnList) {
        List<String> colum = Lists.newLinkedList();
        List<String> values = Lists.newLinkedList();
        List<Object> value = Lists.newLinkedList();
        columnList
                .forEach(i -> {
                    colum.add(i.getName());
                    value.add(DataTypeIndex.of(i.getSqlType(), i.getValue()));
                    values.add("?");
                });
        if (insertOrUpdate) {
            value.addAll(Lists.newArrayList(value));
        }
        return sqlData(insertOrUpdate, cmd.getTableName(), colum, values, value);
    }

    /**
     * sql 处理
     *
     * @param insertOrUpdate 是否insert
     * @param tableName      表名
     * @param colum          字段列表
     * @param values         数据
     * @param value          数据
     * @return 结果
     */
    private SqlData sqlData(boolean insertOrUpdate, String tableName, List<String> colum, List<String> values, List<Object> value) {
        return sqlData(insertOrUpdate, tableName, colum, values, value, false);
    }

    /**
     * sql 处理
     *
     * @param insertOrUpdate 是否insert
     * @param tableName      表名
     * @param colum          字段列表
     * @param values         数据
     * @param value          数据
     * @return 结果
     */
    private SqlData sqlData(boolean insertOrUpdate, String tableName, List<String> colum, List<String> values, List<Object> value, boolean setValue) {
        return SqlData.builder()
                .data(value)
                .sql(insertOrUpdate ?
                        String.format(INSERT_OR_UPDATE_SQL, tableName, String.join(",", colum), String.join(",", values), setValue ? onUpdateSql(colum, value) : onUpdateSql(colum)) :
                        String.format(INSERT_SQL, tableName, String.join(",", colum), String.join(",", values)))
                .build();
    }

    /**
     * insert or update 语句
     *
     * @param insertOrUpdate 是否开启insert or update
     * @param cmd            参数
     * @param columnList     字段/数据列表
     * @return sql数据
     */
    public SqlData formatInsertSql2(boolean insertOrUpdate, Cmd cmd, List<ColumnData> columnList) {
        List<String> colum = Lists.newLinkedList();
        List<String> values = Lists.newLinkedList();
        List<Object> value = Lists.newLinkedList();
        columnList.forEach(i -> {
            colum.add(i.getName());
            value.add(i.getValue());
            values.add("?");
        });
        return sqlData(insertOrUpdate, cmd.getTableName(), colum, values, value, true);
    }

    /**
     * insert or Update 修改字段 语句
     *
     * @param cmd        参数
     * @param columnList 字段/数据列表
     * @return sql数据
     */
    public SqlData formatUpdateOrInsertSql(Cmd cmd, List<CanalEntry.Column> columnList) {
        List<String> colum = Lists.newLinkedList();
        List<String> values = Lists.newLinkedList();
        List<Object> value = Lists.newLinkedList();
        List<String> updateColum = Lists.newLinkedList();
        List<Object> updateValue = Lists.newLinkedList();
        columnList.forEach(i -> {
            colum.add(i.getName());
            value.add(DataTypeIndex.of(i.getSqlType(), i.getValue()));
            values.add("?");
            if (i.getUpdated() || !cmd.getTypeProperties().getIsUpdateColum()) {
                updateColum.add(i.getName());
                updateValue.add(i.getValue());
            }
        });
        return SqlData.builder()
                .data(value)
                .sql(String.format(INSERT_OR_UPDATE_SQL, cmd.getTableName(), String.join(",", colum), String.join(",", values), onUpdateSql(updateColum, updateValue)))
                .build();
    }


    /**
     * 修改 语句
     *
     * @param cmd        表名称
     * @param columnList 字段/数据列表
     * @return sql数据
     */
    public SqlData formatUpdateSql(Cmd cmd, List<CanalEntry.Column> columnList) {
        List<String> updateColum = Lists.newLinkedList();
        List<Object> value = Lists.newLinkedList();
        StringBuilder id = new StringBuilder();
        List<Object> idList = Lists.newLinkedList();
        columnList.forEach(i -> {
            if (i.getUpdated() || !cmd.getTypeProperties().getIsUpdateColum()) {
                updateColum.add(String.format("%s = ?", i.getName()));
                value.add(DataTypeIndex.of(i.getSqlType(), i.getValue()));
            }
            if (i.getIsKey()) {
                id.append(i.getName()).append(StringPool.EQUALS).append(StringPool.QUESTION_MARK);
                idList.add(DataTypeIndex.of(i.getSqlType(), i.getValue()));
            }
        });
        if (id.length() == 0) {
            log.warn("pk is null,{}", cmd.getTableName());
            return null;
        }
        value.addAll(idList);
        return SqlData.builder()
                .data(value)
                .sql(String.format(UPDATE_SQL, cmd.getTableName(),
                        String.join(",", updateColum),
                        id))
                .build();
    }

    /**
     * 删除 语句
     *
     * @param cmd        表名称
     * @param columnList 字段/数据列表
     * @return sql数据
     */
    public SqlData formatRemoveSql(Cmd cmd, List<CanalEntry.Column> columnList) {
        List<Object> value = Lists.newLinkedList();
        StringBuilder id = new StringBuilder();
        columnList.forEach(i -> {
            if (i.getIsKey()) {
                id.append(i.getName()).append(StringPool.EQUALS).append(StringPool.QUESTION_MARK);
                value.add(DataTypeIndex.of(i.getSqlType(), i.getValue()));
            }
        });
        if (CollectionUtils.isEmpty(value)) {
            log.error("remove error,table:{},not key set", cmd.getTableName());
            return null;
        }
        return SqlData.builder()
                .data(value)
                .sql(String.format(DELETE_SQL, cmd.getTableName(), id))
                .build();
    }


}
